import { HttpClient } from '@angular/common/http'
import { Injectable } from '@angular/core'
import { Observable } from 'rxjs'
import { AdRequestPromiseParams, AdRequestObservableParams } from './http.interface'
import { catchError, finalize, mergeMap } from 'rxjs/operators'
import { AdHttpInterceptorImpl } from './http.interceptor.impl'
import { NzNotificationService } from 'ng-zorro-antd/notification'
import { AdLoadingService } from 'monk-design/loading'
@Injectable()
export class AdHttpService {
  private interceptor = new AdHttpInterceptorImpl(this)
  constructor(
    private http: HttpClient,
    public notify: NzNotificationService,
    public _AdLoadingService: AdLoadingService
  ) {}

  /**
   * get
   * @param parameter
   */
  public get<T = any>(parameter: Omit<AdRequestPromiseParams, 'method'>): Promise<T>
  public get<T = any>(parameter: Omit<AdRequestObservableParams, 'method'>): Observable<T>
  public get<T = any>(
    parameter: Omit<AdRequestPromiseParams | AdRequestObservableParams, 'method'>
  ): Promise<T> | Observable<T> {
    return this.request({ method: 'GET', ...parameter } as any)
  }

  /**
   * post
   * @param parameter
   */
  public post<T = any>(parameter: Omit<AdRequestPromiseParams, 'method'>): Promise<T>
  public post<T = any>(parameter: Omit<AdRequestObservableParams, 'method'>): Observable<T>
  public post<T = any>(
    parameter: Omit<AdRequestPromiseParams | AdRequestObservableParams, 'method'>
  ): Promise<T> | Observable<T> {
    return this.request({ method: 'POST', ...parameter } as any)
  }

  /**
   * put
   * @param parameter
   */
  public put<T = any>(parameter: Omit<AdRequestPromiseParams, 'method'>): Promise<T>
  public put<T = any>(parameter: Omit<AdRequestObservableParams, 'method'>): Observable<T>
  public put<T = any>(
    parameter: Omit<AdRequestPromiseParams | AdRequestObservableParams, 'method'>
  ): Promise<T> | Observable<T> {
    return this.request({ method: 'PUT', ...parameter } as any)
  }

  /**
   * delete
   * @param parameter
   */
  public delete<T = any>(parameter: Omit<AdRequestPromiseParams, 'method'>): Promise<T>
  public delete<T = any>(parameter: Omit<AdRequestObservableParams, 'method'>): Observable<T>
  public delete<T = any>(
    parameter: Omit<AdRequestPromiseParams | AdRequestObservableParams, 'method'>
  ): Promise<T> | Observable<T> {
    return this.request({ method: 'DELETE', ...parameter } as any)
  }

  public request<T = any>(parameter: AdRequestPromiseParams): Promise<T>
  public request<T = any>(parameter: AdRequestObservableParams): Observable<T>
  public request<T = any>(
    parameter: AdRequestPromiseParams | AdRequestObservableParams
  ): Observable<T> | Promise<T> {
    parameter = { ...this.interceptor.defaultParameter, ...parameter }
    const {
      url,
      method,
      isPromise,
      data,
      observe,
      responseType,
      downloadProgress,
      uploadProgress,
    } = parameter
    const [paramObj, headersObj] = this.interceptor.requestBefore(parameter)
    const response: Observable<any> = this.http
      .request(method, url, {
        body: data,
        params: paramObj,
        headers: headersObj,
        observe,
        responseType,
        reportProgress: observe === 'events' && Boolean(downloadProgress || uploadProgress),
      })
      .pipe(
        mergeMap((value) => this.interceptor.responseHandle(value, parameter)),
        catchError((error) => this.interceptor.catchError(error, parameter)),
        finalize(() => this.interceptor.finalize(parameter))
      )
    if (isPromise) {
      return response.toPromise()
    }
    return response
  }

  public jsonp<T = any>(parameter: AdRequestPromiseParams & { callback: string }): Promise<T>
  public jsonp<T = any>(parameter: AdRequestObservableParams & { callback: string }): Observable<T>
  public jsonp<T = any>(
    parameter: (AdRequestPromiseParams | AdRequestObservableParams) & { callback: string }
  ): Observable<T> | Promise<T> {
    parameter = { ...this.interceptor.defaultParameter, ...parameter }
    const { url, isPromise } = parameter
    const [paramObj] = this.interceptor.requestBefore(parameter)
    let joinChar = ''
    if (url.includes('?') && paramObj.toString()) {
      joinChar = '&'
    } else if (paramObj.toString()) {
      joinChar = '?'
    }
    const response: Observable<any> = this.http
      .jsonp(`${url}${joinChar}${paramObj.toString()}`, parameter.callback)
      .pipe(
        mergeMap((value) => this.interceptor.responseHandle(value, parameter)),
        catchError((error) => this.interceptor.catchError(error, parameter)),
        finalize(() => this.interceptor.finalize(parameter))
      )
    if (isPromise) {
      return response.toPromise()
    }
    return response
  }
}
